`timescale    1ps/1ps
module v8_com_ctrl_mcu_04
#(parameter
    DEVICE_TYPE = 0    //0：接收卡   1：附属设备  当作为接收卡或者附属设备时，解析命令位置会发生变化，具体见协议
)
(
    input       wire        sclk,        //125M
    input       wire        resetb,
    input       wire        time_1ms_sync,
    input       wire        comm_en,

    input       wire        rec_flag,
    input       wire        rec_error,
    input       wire [7:0]  rec_data,
    input       wire [1:0]  rec_vendor, //0:无效包 1：UDP包

    output      reg         send_flag,
    output      reg         pre_flag,
    output      wire [7:0]  send_data,
    input       wire        blank_flag,
    input       wire        redu_flag,

    input       wire        clk_mcu, //62.5M

    output      wire        EXT_RAM_EN            ,
    output      wire        EXT_RAM_WR            ,
    output      wire [3:0]  EXT_RAM_BYTE_EN   ,
    output      wire [13:0] EXT_RAM_ADDR     ,
    output      wire [31:0] EXT_RAM_WDATA    ,
    input       wire [31:0] EXT_RAM_RDATA    ,


    output      reg         packet_load       ,
    output      reg         interrupt        ,

    input       wire        mcu_rec_end               ,
    input       wire [1:0]  fpga_flag_next            ,


    output      wire [7:0]  tout             
);

assign EXT_RAM_BYTE_EN = 4'b1111;
assign EXT_RAM_ADDR[13:8]  = 6'b111110 ;


reg     [ 7: 0]     mcu_addr            ;
reg                 mcu_head_end        ;
reg                 mcu_data_rec_end    ;
reg                 mcu_data_send_end   ;
reg                 mcu_rec_flag        ;
reg                 mcu_rec_flag_d      ;
reg                 mcu_send_flag       ;
reg                 mcu_send_flag_d     ;
reg                 mcu_en              ;
reg                 mcu_wr              ;
reg     [ 7: 0]     ram_addr            ;
reg     [1:0]       com_state_MCU_RECEIVE;
reg     [1:0]       com_state_MCU_SEND  ;

reg     [1:0]       mcu_data_rec_end_d  ;
reg                 com_state_MCU_RECEIVE_END;
reg     [1:0]       mcu_data_send_end_d ;
reg                 com_state_MCU_SEND_end  ;
reg                 mcu_head_end_d ;


assign EXT_RAM_ADDR[7:0]  = mcu_addr[7:0];
assign EXT_RAM_EN = mcu_en;
assign EXT_RAM_WR = mcu_wr;

//************************************************/
//        !!设备类型!!
//************************************************/

//************************************************/
//        参数定义
//************************************************/
//************************************************/
localparam  Preamble_OFFSET    = 23;
localparam  PACK_TYPE_FIRST    = 24 - 1;
localparam  ASI_DEPTH_FIRST    = 26 - 1;
localparam  PACK_DEPTH_FIRST   = 28 - 1;
localparam  COM_PACK_SEL_TYPE  = 30 - 1;
localparam  COM_PACK_SEL_ADDR  = 31 - 1;
localparam  COM_PACK_ASI_ADDR  = 33 - 1;
localparam  COM_PACK_COMMAND   = 38 - 1;
localparam  COM_PACK_ADDR      = 40 - 1;
localparam  COM_PACK_HEAD_END  = 43 - 1;

localparam  TIMEOUT            = 15;

//*************state******************************
localparam  IDLE_STATE         = 8'h01;
localparam  WAIT_STATE         = 8'h02;
localparam  COM_RECEIVE        = 8'h04;
localparam  MCU_RECEIVE        = 8'h08;
localparam  MCU_SEND           = 8'h10;
localparam  SEND_FEEDBACK      = 8'h20;
localparam  WAIT_MCU           = 8'h40;
localparam  WAIT_TEST          = 8'h80;

localparam  IDLE_STATE_ID         = 8'h00;
localparam  WAIT_STATE_ID         = 8'h01;
localparam  COM_RECEIVE_ID        = 8'h02;
localparam  MCU_RECEIVE_ID        = 8'h03;
localparam  MCU_SEND_ID           = 8'h04;
localparam  SEND_FEEDBACK_ID      = 8'h05;
localparam  WAIT_MCU_ID           = 8'h06;
localparam  WAIT_TEST_ID          = 8'h07;

///**************命令类型****************
localparam  MEM_READ           = 8'h08;
localparam  MEM_WRITE          = 8'h04;

//**********************************************/
//        信号定义
/************************************************/
reg         buf_wen;
reg  [10:0] buf_addr;
reg  [7:0]  buf_wdata;
wire [7:0]  buf_rdata;

reg  [7:0]  com_state;
reg  [8:0]  data_count;
reg         head_flag,head_end,com_packet_flag;
reg  [7:0]  current_depth_c, current_depth;
reg         device_active,read_command,write_command,data_end;
reg  [23:0] op_page_addr;
reg         com_rec_temp,send_flag_a,send_flag_b;
reg         rec_data_wen;
reg         buf_wen_a,buf_wen_b;
reg  [9:0]  buf_addr_a;
reg  [9:0]  buf_addr_b;
reg  [1:0]  ack_head_type_t1,ack_head_type;
reg         udp_head,udp_head_end;
reg         fpga_send_end_flag;
reg         send_timeout;
reg  [3:0]  ms_count;
wire [2:0]  current_device;

//*************************************************/
//        通讯buf
//*************************************************/
//com_buf 140516修改 UDP时desport为5AA5
// swsr_1k8_tp_com_mif    com_buf (
ram_1024x8_256x32    com_buf (
    .clock_b    ( clk_mcu ),
    .wren_b     ( buf_wen_a ),
    .address_b  ( ram_addr[7:0] ), //( buf_addr_a[7:0] ), //ram_addr[7:0]),
    .data_b     ( EXT_RAM_RDATA[31:0] ),
    .q_b        ( EXT_RAM_WDATA[31:0]  ),

    .clock_a    ( sclk ),
    .wren_a     ( rec_data_wen ), //( buf_wen_b ),
    .address_a  ( buf_addr_b ),
    .data_a     ( rec_data ),
    .q_a        ( send_data)//(  )
    );
    

//************************************************/
//        状态控制
//************************************************/
//**************主状态机*******************
always@(posedge sclk or negedge resetb)
    if(resetb == 0)begin
        com_state <= IDLE_STATE;
        interrupt <= 0;
    end
    else
        case(com_state)
            IDLE_STATE: begin
                if (comm_en==0)
                    com_state <= WAIT_STATE;
                else if (rec_flag==1)
                    com_state <= COM_RECEIVE;
                interrupt <= 0;
            end

            WAIT_STATE:begin
                if(rec_flag==0 && comm_en==1)
                    com_state <= IDLE_STATE;
                interrupt <= 0;
            end

            COM_RECEIVE:begin    //收到的数据去掉了udp包头
                if (rec_flag==0) begin
                    if(rec_error==1 || com_packet_flag==0 || device_active==0 )
                        com_state <= IDLE_STATE;
                    else begin
                            com_state <= MCU_RECEIVE;
                    end
                end
            end

            MCU_RECEIVE:
                if( com_state_MCU_RECEIVE_END)
                    com_state <= WAIT_MCU;

            WAIT_MCU:begin
                interrupt <= 1;
                if(mcu_rec_end)begin
                    if (fpga_flag_next[1:0] ==2'b10) begin
                        com_state <= MCU_SEND;
                    end
                    else begin
                        com_state <= WAIT_STATE;
                    end
                end
            end

            MCU_SEND:
                if( fpga_send_end_flag==1 )
                begin
                    if(redu_flag==0)    //单向级联模式
                        com_state <= SEND_FEEDBACK;
                    else if(blank_flag==1 || send_timeout==1)    //双口冗余模式，等空闲包
                        com_state <= SEND_FEEDBACK;
                end

            SEND_FEEDBACK:
                if(data_end == 1)
                    com_state <= WAIT_STATE;

            default:    com_state <= WAIT_STATE;
        endcase

//**************通讯包有效数据计数*******************
//包头标志
always @(posedge sclk)
    // if (com_state==IDLE_STATE || com_state==MCU_SEND)
    if (com_state[ IDLE_STATE_ID ] || com_state[ MCU_SEND_ID ])
        head_flag<=1;
    else if (head_end==1)
        head_flag<=0;

//包头结束标志
always @(posedge sclk)
    if (head_flag==1 && data_count==COM_PACK_HEAD_END-1 && udp_head==0)
        head_end<=1;
    else
        head_end<=0;

//数据计数
always @(posedge sclk)
    // if (com_state==IDLE_STATE || udp_head_end==1)
     if (com_state[ IDLE_STATE_ID ] || udp_head_end==1)
        data_count<=23;
    else if (head_end==1 )//|| com_state==MCU_SEND)
        data_count<=0;
    // else if(com_state==MCU_SEND) begin
    else if(com_state[ MCU_SEND_ID ]) begin
        if(redu_flag==1)
            data_count<=44;    //去掉部分包头，用于冗余替换空闲包数据
        else
            data_count<=0;
        end
    // else if (com_state==COM_RECEIVE || com_state==SEND_FEEDBACK)
    else if (com_state[ COM_RECEIVE_ID ] || com_state[ SEND_FEEDBACK_ID ])
        data_count<=data_count+1'b1;

always @(posedge sclk or negedge resetb)
    if(resetb==0)
        udp_head<=0;
    // else if(com_state==MCU_SEND && ack_head_type==1)
    else if(com_state[ MCU_SEND_ID ] && ack_head_type==1)
        udp_head<=1'b1;
    else if(udp_head_end==1)
        udp_head<=0;

always @(posedge sclk or negedge resetb)
    if(resetb==0)
        udp_head_end<=0;
    // else if(com_state==SEND_FEEDBACK && udp_head==1 && data_count==49)
    else if(com_state[ SEND_FEEDBACK_ID ] && udp_head==1 && data_count==49)
        udp_head_end<=1'b1;
    else
        udp_head_end<=0;
//**************通讯包解码*******************
//通讯包识别
assign current_device=DEVICE_TYPE;

always @(posedge sclk)
    // if (com_state!=COM_RECEIVE)
    if (com_state[ COM_RECEIVE_ID ] != 1)
        com_packet_flag<=0;
    else if (head_flag==1 && data_count[5:0]==PACK_TYPE_FIRST && rec_data==8'hC5 && rec_vendor==1)
        com_packet_flag<=1'b1;

//设备级联编号识别
always @(posedge sclk)
    // if (com_state!=COM_RECEIVE)
    if (com_state[ COM_RECEIVE_ID ] != 1 )
        current_depth_c<=0;
    else if (current_device==0 && com_state==COM_RECEIVE && head_flag==1 && data_count[5:0]==PACK_DEPTH_FIRST)//接收卡级联深度
        current_depth_c<=rec_data;
    else if (current_device==1 && com_state==COM_RECEIVE && head_flag==1 && data_count[5:0]==ASI_DEPTH_FIRST)//附属设备级联深度
        current_depth_c<=rec_data;

//设备编号选择
always @(posedge sclk)
    current_depth <= current_depth_c;

//设备选择识别
always @(posedge sclk)
    // if (com_state!=COM_RECEIVE)
    if (com_state[ COM_RECEIVE_ID ] == 0 )
        device_active<=0;
    else if(current_device==0)    //接收卡
    begin
        if (head_flag==1 && data_count[5:0]==COM_PACK_SEL_TYPE && rec_data[0]==1)
            device_active<=1'b1;
        else if (head_flag==1 && data_count[5:0]==COM_PACK_SEL_ADDR && rec_data==current_depth && current_depth!=0)
            device_active<=1'b1;
    end
    else if(current_device==1)    //附属设备
    begin
        if (head_flag==1 && data_count[5:0]==COM_PACK_SEL_TYPE && rec_data[1]==1)
            device_active<=1'b1;
        else if (head_flag==1 && data_count[5:0]==COM_PACK_ASI_ADDR && rec_data==current_depth && current_depth!=0)
            device_active<=1'b1;
    end


/***************应答包长度控制**********************/
always @(posedge sclk)
    if (data_count==255-1)
        data_end<=1'b1;
    else
        data_end<=0;

reg    com_packet_t;
reg    [7:0]    com_state_t;
always @(posedge sclk)begin
    com_packet_t<=com_packet_flag;
    com_state_t<=com_state;
end

always @(posedge sclk or negedge resetb)
    if(resetb==0)
        ack_head_type_t1<=0;
    else if (com_packet_t==0 && com_packet_flag==1)
        ack_head_type_t1<=rec_vendor;

always @(posedge sclk or negedge resetb)
    if(resetb==0)
        packet_load<=0;
    else if (com_packet_t==0 && com_packet_flag==1 )
        packet_load<=1;
    else 
        packet_load<=0;
always @(posedge sclk or negedge resetb)
    if(resetb==0)
        ack_head_type<=0;
    // else if(com_state_t==WAIT_MCU && com_state==MCU_SEND)
    else if( com_state_t[ WAIT_MCU_ID ] && com_state[ MCU_SEND_ID ] )
        ack_head_type<=ack_head_type_t1;

//**********************************************************************/
//        应答包发送
//**********************************************************************/
always @(posedge sclk or negedge resetb)
    if (resetb==0)
        send_flag<= 0;
    // else if (com_state==SEND_FEEDBACK)
    else if ( com_state[ SEND_FEEDBACK_ID ] )
        send_flag<= 1'b1;
    else
        send_flag<=0;

always @*
    // if (com_state==SEND_FEEDBACK && head_flag==1 && data_count>=1 && data_count<=8)
    if (com_state[ SEND_FEEDBACK_ID ] && head_flag==1 && data_count>=1 && data_count<=8)
        pre_flag<= 1'b1;
    else
        pre_flag<= 0;


always @(posedge sclk)
    // if (com_state!=COM_RECEIVE)
    if (com_state[ COM_RECEIVE_ID ] == 0)
        rec_data_wen<= 0;
    else if (data_count==COM_PACK_SEL_TYPE-1)
        rec_data_wen<= 1'b1;
    else if (data_end==1)
        rec_data_wen<= 0;

always @*
    buf_wen_b<=rec_data_wen;

always @*
    buf_addr_a[7:0] <= ram_addr[7:0];

always @*
    // if(com_state==COM_RECEIVE)
    if(com_state[ COM_RECEIVE_ID ])
        buf_addr_b <= {head_flag,1'b0,data_count[7:0]};
    else if(udp_head)
        buf_addr_b <= {2'b10,1'b1,data_count[6:0]};
    else if(head_flag)
        buf_addr_b <= {2'b10,data_count[7:0]};
    // else if(com_state==SEND_FEEDBACK)
    else if(com_state[ SEND_FEEDBACK_ID ])
        buf_addr_b <= {2'b01,data_count[7:0]};
    else
        buf_addr_b <= 'd0;



//mcu 接收

always @(posedge clk_mcu or  negedge resetb)
    if(resetb==0 )
        com_state_MCU_RECEIVE[1:0] <= 0;
    else
        com_state_MCU_RECEIVE[1:0] <= {com_state_MCU_RECEIVE[0] ,  com_state[ MCU_RECEIVE_ID ]};

always @(posedge clk_mcu or  negedge resetb)
    if(resetb==0 )
        mcu_rec_flag<= 0;
    else if(com_state_MCU_RECEIVE[1:0] == 2'b01 )
        mcu_rec_flag <= 1;
    else
        mcu_rec_flag<= 0;

always @(posedge clk_mcu or  negedge resetb)
    if(resetb==0 )
        mcu_en <=  0;
    else if(mcu_rec_flag  || mcu_send_flag)
        mcu_en <=  1;
    else if( mcu_data_rec_end || mcu_data_send_end)
        mcu_en <=  0;


always @(posedge clk_mcu or  negedge resetb)
    if(resetb==0 )
        mcu_rec_flag_d<= 0;
    else
        mcu_rec_flag_d<= mcu_rec_flag;

always @(posedge clk_mcu or  negedge resetb)
    if(resetb==0 )
        mcu_wr <= 0;
    else if(mcu_rec_flag_d)
        mcu_wr <= 1 ;
    else if(mcu_data_rec_end)
        mcu_wr <= 0;

//命令头 结束
always @(posedge clk_mcu or  negedge resetb)
    if(resetb==0)
        mcu_head_end<= 0;
    // if (com_state!=MCU_RECEIVE)
    else if ( mcu_wr && ram_addr[7:0]==8'h89 )
        mcu_head_end<= 1;
    else
        mcu_head_end<= 0;

//mcu 接收数据结束
always @(posedge clk_mcu or  negedge resetb)
    if( resetb==0)
        mcu_data_rec_end <= 0;
    // if(com_state!=MCU_RECEIVE)
    else if ( mcu_wr && ram_addr[7:0]==8'h3f )
        mcu_data_rec_end <= 1;
    else
        mcu_data_rec_end <= 0;

// ram_addr
always @(posedge clk_mcu or  negedge resetb)
    if( resetb==0)
        ram_addr[7:0]<= 8'h87;
    else if ( mcu_rec_flag  )
        ram_addr[7:0]<= 8'h87;
    else if( mcu_send_flag )
        ram_addr[7:0]<= 8'h3f;
    else if( mcu_head_end )
        ram_addr[7:0]<= 8'h00;
    // else if (com_state==MCU_RECEIVE || com_state==MCU_SEND)  begin
    else if (  mcu_en )
         ram_addr[7:0] <= ram_addr[7:0] +1;

always @(posedge clk_mcu or  negedge resetb)
    if(resetb ==0)
        mcu_head_end_d <= 0;
    else
        mcu_head_end_d <= mcu_head_end;
//mcu_addr
always @(posedge clk_mcu or  negedge resetb)
    if( resetb==0)
        mcu_addr[7:0] <= 8'h86;
    else if ( mcu_rec_flag)
        mcu_addr[7:0]<= 8'h86;
    else if(mcu_head_end_d)
        mcu_addr[7:0]<= 8'h00;
    else if( mcu_send_flag )
        mcu_addr[7:0]<= 8'h40;
    else if ( mcu_en )
        mcu_addr[7:0]<=mcu_addr[7:0]+1'b1;

//mcu 发送

always @(posedge clk_mcu or  negedge resetb)
    if(resetb==0 )
        com_state_MCU_SEND[1:0] <= 0;
    else
        com_state_MCU_SEND[1:0] <= {com_state_MCU_SEND[0] ,  com_state[ MCU_SEND_ID ]};

always @(posedge clk_mcu or  negedge resetb)
    if(resetb==0 )
        mcu_send_flag<= 0;
    else if(com_state_MCU_SEND[1:0] == 2'b01 )
        mcu_send_flag <= 1;
    else
        mcu_send_flag<= 0;


always @(posedge clk_mcu or  negedge resetb)
    if( resetb==0 )
        mcu_send_flag_d<= 0;
    else
        mcu_send_flag_d<= mcu_send_flag;

always @(posedge clk_mcu or  negedge resetb)
    if( resetb==0)
        mcu_data_send_end <= 0;
    // if(com_state!=MCU_RECEIVE)
    else if ( mcu_en && ram_addr[7:0]==8'hbe )
        mcu_data_send_end <= 1;
    else
        mcu_data_send_end <= 0;

always @(posedge clk_mcu or  negedge resetb)
    if( resetb==0 )
        buf_wen_a <= 0 ;
    else if( mcu_send_flag_d )
        buf_wen_a <=1 ;
    else if( mcu_data_send_end )
        buf_wen_a <= 0 ;



always @(posedge sclk or  negedge resetb)
    if( resetb==0 )
        mcu_data_rec_end_d[1:0] <= 0;
    else
       mcu_data_rec_end_d[1:0] <= {mcu_data_rec_end_d[0] , mcu_data_rec_end };

always @(posedge sclk or  negedge resetb)
    if( resetb==0 )
       com_state_MCU_RECEIVE_END <= 0;
    else if ( mcu_data_rec_end_d[1:0] == 2'b01 )
        com_state_MCU_RECEIVE_END <= 1;
    else
        com_state_MCU_RECEIVE_END <= 0;

always @(posedge sclk or  negedge resetb)
    if( resetb==0 )
        mcu_data_send_end_d[1:0] <= 0;
    else
       mcu_data_send_end_d[1:0] <= {mcu_data_send_end_d[0] , mcu_data_send_end };

always @(posedge sclk or  negedge resetb)
    if( resetb==0 )
       com_state_MCU_SEND_end <= 0;
    else if ( mcu_data_send_end_d[1:0] == 2'b01 )
        com_state_MCU_SEND_end <= 1;
    else
        com_state_MCU_SEND_end <= 0;

/************************************************/
//        应答包 超时反馈控制
/************************************************/
always @(posedge sclk)
    // if(com_state!=MCU_SEND )
    if(com_state[ MCU_SEND_ID ] )
        if( com_state_MCU_SEND_end )
            fpga_send_end_flag<=1'b1;
    else
        fpga_send_end_flag<=0;

always @(posedge sclk or negedge resetb)
    if(resetb==0)
        ms_count<=0;
    else if(time_1ms_sync==1)begin
        if(fpga_send_end_flag==0)
            ms_count<=0;
        else if(ms_count==TIMEOUT-1)
            ms_count<=0;
        else
            ms_count<=ms_count+1'b1;
    end

always @(posedge sclk)
    if(time_1ms_sync==1 && ms_count==TIMEOUT-1)
        send_timeout<=1'b1;
    else
        send_timeout<=0;

/************************************************/
//        测试信号
/************************************************/
assign    tout= com_state;

endmodule

